/**
 * 基于JQM的功能覆盖和扩展
 * @author Levin
 * @version 1.0
 */
JF.M("jqmExt",(
 
 function(){
	var p={},pub={};

	p.changePage={
		//jqm related data
		data:{
			isPageTransitioning:false,
			pageTransitionQueue:[],
			nextPageRole:null,
			$activeClickedLink:null,
			focusable : "[tabindex],a,button:visible,select:visible,input"
		},
		//internal jqm methods
		jqmMethods:{
			//remove active classes after page transition or error
			removeActiveLinkClass:function ( forceRemoval ){

				/* custom code */
				var $activeClickedLink= p.changePage.data.$activeClickedLink;
				/* /custom code */

				if( !!$activeClickedLink && (!$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval )){
					$activeClickedLink.removeClass( $.mobile.activeBtnClass );
				}
				$activeClickedLink = null;

				/* custom code */
				p.changePage.synchronize({
					$activeClickedLink:null
				});
				/* /custom code */
			},//removeActiveLinkClass see line 2212 in jqm
			/* direct focus to the page title, or otherwise first focusable element */
			reFocus:function(page){
				var lastClicked = page.jqmData( "lastClicked" );
					
				if( lastClicked && lastClicked.length ){
					lastClicked.focus();
				}
				else {
					var pageTitle = page.find( ".ui-title:eq(0)" );
					
					if( pageTitle.length ){
						pageTitle.focus();
					}
					else{
						page.find( p.changePage.data.focusable ).eq(0).focus();
					}
				}			
			}//reFocus see line 2197
		},

		backup:$.mobile.changePage,
		synchronize:function(data){
			for(var d in data){
				if(!d) continue;
				this.data[d]=data[d];
			};
		},
		action:function( to, transition, reverse, changeHash, fromHashChange ){

			//from is always the currently viewed page
			var toIsArray = $.type(to) === "array",
				toIsObject = $.type(to) === "object",
				from = toIsArray ? to[0] : $.mobile.activePage;

				to = toIsArray ? to[1] : to;

			/* custom code */
			//Note: this will be referenced to the $.mobile sometimes,so advoid using it
			var path=$.mobile.path,
				urlHistory=$.mobile.urlHistory,
				base = $.mobile.base,
				nextPageRole=p.changePage.data.nextPageRole,
				isPageTransitioning=p.changePage.data.isPageTransitioning,
				pageTransitionQueue=p.changePage.data.pageTransitionQueue,
				$activeClickedLink=p.changePage.data.$activeClickedLink,
				removeActiveLinkClass=p.changePage.jqmMethods.removeActiveLinkClass,
				reFocus=p.changePage.jqmMethods.reFocus,
				$window=$(window),
				$html=$("html"),
				getData=function(){
					var _d=[{
						'nextPageRole':nextPageRole,
						'isPageTransitioning':isPageTransitioning,
						'pageTransitionQueue':pageTransitionQueue,
						'$activeClickedLink':$activeClickedLink,
						'transition':transition,
						'to':to,
						'from':from,
						'reverse':reverse
					}];
					return _d;
				};
			/* /custom code */

			var url = $.type(to) === "string" ? path.stripHash( to ) : "",
				fileUrl = url,
				data,
				type = 'get',
				isFormRequest = false,
				duplicateCachedPage = null,
				currPage = urlHistory.getActive(),
				back = false,
				forward = false,
				pageTitle = document.title;


			// If we are trying to transition to the same page that we are currently on ignore the request.
			// an illegal same page request is defined by the current page being the same as the url, as long as there's history
			// and to is not an array or object (those are allowed to be "same")
			if( currPage && urlHistory.stack.length > 1 && currPage.url === url && !toIsArray && !toIsObject ) {
				return;
			}
			else if(isPageTransitioning) {
				pageTransitionQueue.unshift(arguments);
				return;
			}

			isPageTransitioning = true;

			// if the changePage was sent from a hashChange event guess if it came from the history menu
			// and match the transition accordingly
			if( fromHashChange ){
				urlHistory.directHashChange({
					currentUrl: url,
					isBack: function(){
						forward = !(back = true);
						reverse = true;
						transition = transition || currPage.transition;
					},
					isForward: function(){
						forward = !(back = false);
						transition = transition || urlHistory.getActive().transition;
					}
				});

				//TODO forward = !back was breaking for some reason
			}

			if( toIsObject && to.url ){
				url = to.url;
				data = to.data;
				type = to.type;
				isFormRequest = true;
				//make get requests bookmarkable
				if( data && type === 'get' ){
					if($.type( data ) === "object" ){
						data = $.param(data);
					}

					url += "?" + data;
					data = undefined;
				}
			}

			//reset base to pathname for new request
			if(base){ base.reset(); }

			//kill the keyboard
			if( window.document.activeElement ){
				$( window.document.activeElement || "" ).add( "input:focus, textarea:focus, select:focus" ).blur();
			}

			function defaultTransition(){
				if(transition === undefined){
					transition = ( nextPageRole && nextPageRole === 'dialog' ) ? 'pop' : $.mobile.defaultTransition;
				}
			}

			function releasePageTransitionLock(){
				isPageTransitioning = false;
				if(pageTransitionQueue.length>0) {
					$.mobile.changePage.apply($.mobile, pageTransitionQueue.pop());
				}
			}

			//function for transitioning between two existing pages
			function transitionPages() {

				/* custom code */
				var _transData=null;
				/* /custom code */

				$.mobile.silentScroll();

				//get current scroll distance
				var currScroll = $window.scrollTop(),
						perspectiveTransitions = [ "flip" ],
						pageContainerClasses = [];

				//support deep-links to generated sub-pages
				if( url.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ){
					to = $( ":jqmData(url='" + url + "')" );
				}

				if( from ){
					//set as data for returning to that spot
					from
						.jqmData( "lastScroll", currScroll)
						.jqmData( "lastClicked", $activeClickedLink);
					//trigger before show/hide events
					from.data( "page" )._trigger( "beforehide", null, { nextPage: to } );
				}
				to.data( "page" )._trigger( "beforeshow", null, { prevPage: from || $("") } );

				function pageChangeComplete(){

					if( changeHash !== false && url ){
						//disable hash listening temporarily
						urlHistory.ignoreNextHashChange = false;
						//update hash and history
						path.set( url );
					}

					//if title element wasn't found, try the page div data attr too
					var newPageTitle = to.jqmData("title") || to.find(".ui-header .ui-title" ).text();
					if( !!newPageTitle && pageTitle == document.title ){
						pageTitle = newPageTitle;
					}

					//add page to history stack if it's not back or forward
					if( !back && !forward ){
						urlHistory.addNew( url, transition, pageTitle, to );
					}

					//set page title
					document.title = urlHistory.getActive().title;

					removeActiveLinkClass();

					//jump to top or prev scroll, sometimes on iOS the page has not rendered yet.  I could only get by this with a setTimeout, but would like to avoid that.
					$.mobile.silentScroll( to.jqmData( "lastScroll" ) );

					reFocus( to );

					//trigger show/hide events
					if( from ){
						from.data( "page" )._trigger( "hide", null, { nextPage: to } );
					}
					//trigger pageshow, define prevPage as either from or empty jQuery obj
					to.data( "page" )._trigger( "show", null, { prevPage: from || $("") } );

					//set "to" as activePage
					$.mobile.activePage = to;

					//if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
					if (duplicateCachedPage !== null) {
						duplicateCachedPage.remove();
					}

					//remove initial build class (only present on first pageshow)
					$html.removeClass( "ui-mobile-rendering" );

					releasePageTransitionLock();

					/* custom code */
					$window.trigger("onPageChanged",getData());
					/* /custom code */

				}

				function addContainerClass(className){
					$.mobile.pageContainer.addClass(className);
					pageContainerClasses.push(className);
				}

				function removeContainerClasses(){
					$.mobile
						.pageContainer
						.removeClass(pageContainerClasses.join(" "));

					pageContainerClasses = [];
				}

				if(transition && (transition !== 'none')){
					$.mobile.pageLoading( true );
					if( $.inArray(transition, perspectiveTransitions) >= 0 ){
						addContainerClass('ui-mobile-viewport-perspective');
					}

					addContainerClass('ui-mobile-viewport-transitioning');

					/* custom code */
					/*
					if( from ){
						from.addClass( transition + " out " + ( reverse ? "reverse" : "" ) );
					}
					to.addClass( $.mobile.activePageClass + " " + transition +
						" in " + ( reverse ? "reverse" : "" ) );

					// callback - remove classes, etc
					to.animationComplete(function() {
						to.add(from).removeClass("out in reverse " + transition );
						if( from ){
							from.removeClass( $.mobile.activePageClass );
						}
						pageChangeComplete();
						removeContainerClasses();
					});
					*/
					/* /custom code */

					var _data=getData();
					_data[0]["pageChangeComplete"]=pageChangeComplete;
					_data[0]["removeContainerClasses"]=removeContainerClasses;
					$window.trigger("onPageTransition",_data);

				}
				else{
					$.mobile.pageLoading( true );
					if( from ){
						from.removeClass( $.mobile.activePageClass );
					}
					to.addClass( $.mobile.activePageClass );
					pageChangeComplete();
				}
			}

			//shared page enhancements
			function enhancePage(){

				//set next page role, if defined
				if ( nextPageRole || to.jqmData('role') === 'dialog' ) {
					url = urlHistory.getActive().url + dialogHashKey;
					if(nextPageRole){
						to.attr( "data-" + $.mobile.ns + "role", nextPageRole );
						nextPageRole = null;
					}
				}

				//run page plugin
				to.page();
			}

			//if url is a string
			if( url ){
				to = $( ":jqmData(url='" + url + "')" );
				fileUrl = path.getFilePath(url);
			}
			else{ //find base url of element, if avail
				var toID = to.attr( "data-" + $.mobile.ns + "url" ),
					toIDfileurl = path.getFilePath(toID);

				if(toID !== toIDfileurl){
					fileUrl = toIDfileurl;
				}
			}

			// ensure a transition has been set where pop is undefined
			defaultTransition();

			// find the "to" page, either locally existing in the dom or by creating it through ajax
			if ( to.length && !isFormRequest ) {
				if( fileUrl && base ){
					base.set( fileUrl );
				}
				enhancePage();
				/* custom code */
				//transitionPages();
				_transData=getData();
				_transData[0].isAjax=false;
				$window.trigger("onPageChanging",_transData);
				transitionPages();
				/* /custom code */
			} else {

				//if to exists in DOM, save a reference to it in duplicateCachedPage for removal after page change
				if( to.length ){
					duplicateCachedPage = to;
				}

				/* custom code */
				_transData=getData();
				_transData[0].isAjax=true;
				$window.trigger("onPageChanging",_transData);
				/* /custom code */

				//$.mobile.pageLoading();

				$.ajax({
					url: fileUrl,
					type: type,
					data: data,
					dataType: "html",
					success: function( html ) {
						//pre-parse html to check for a data-url,
						//use it as the new fileUrl, base path, etc
						var all = $("<div></div>"),
								redirectLoc,

								//page title regexp
								newPageTitle = html.match( /<title[^>]*>([^<]*)/ ) && RegExp.$1,

								// TODO handle dialogs again
								pageElemRegex = new RegExp(".*(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>).*"),
								dataUrlRegex = new RegExp("\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?");


						// data-url must be provided for the base tag so resource requests can be directed to the
						// correct url. loading into a temprorary element makes these requests immediately
						if(pageElemRegex.test(html) && RegExp.$1 && dataUrlRegex.test(RegExp.$1) && RegExp.$1) {
							redirectLoc = RegExp.$1;
						}

						if( redirectLoc ){
							if(base){
								base.set( redirectLoc );
							}
							url = fileUrl = path.getFilePath( redirectLoc );
						}
						else {
							if(base){
								base.set(fileUrl);
							}
						}

						//workaround to allow scripts to execute when included in page divs
						all.get(0).innerHTML = html;
						to = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first();

						//finally, if it's defined now, set the page title for storage in urlHistory
						if( newPageTitle ){
							pageTitle = newPageTitle;
						}

						//rewrite src and href attrs to use a base url
						if( !$.support.dynamicBaseTag ){
							var newPath = path.get( fileUrl );
							to.find( "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]" ).each(function(){
								var thisAttr = $(this).is('[href]') ? 'href' : 'src',
									thisUrl = $(this).attr(thisAttr);


								//if full path exists and is same, chop it - helps IE out
								thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' );

								if( !/^(\w+:|#|\/)/.test(thisUrl) ){
									$(this).attr(thisAttr, newPath + thisUrl);
								}
							});
						}

						//append to page and enhance
						to
							.attr( "data-" + $.mobile.ns + "url", fileUrl )
							.appendTo( $.mobile.pageContainer );

						enhancePage();
						setTimeout(function() { transitionPages(); }, 0);

						/* custom code */
						$window.trigger("onAjaxed",getData());
						/* /custom code */

					},
					error: function() {

						//remove loading message
						$.mobile.pageLoading( true );

						//clear out the active button state
						removeActiveLinkClass(true);

						//set base back to current path
						if( base ){
							base.set( path.get() );
						}

						//release transition lock so navigation is free again
						releasePageTransitionLock();

						//show error message
						$("<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h1>"+ $.mobile.pageLoadErrorMessage +"</h1></div>")
							.css({ "display": "block", "opacity": 0.96, "top": $(window).scrollTop() + 100 })
							.appendTo( $.mobile.pageContainer )
							.delay( 800 )
							.fadeOut( 400, function(){
								$(this).remove();
							});
					}
				});
			}

		},// my own changePage logic
		setup:function(){
			var me=this,
				$window=$(window);
			// override jqm's changePage implementation
			$.mobile.changePage = this.action;

			//set up custom events
			$window.bind("onPageChanged",function(e,data){
				var goOn=true;
				if(p.opts.onPageChanged){
					goOn=p.opts.onPageChanged.call($.mobile,data);
				};
				if(!goOn) return;
				me.synchronize(data||{});
			});

			$window.bind("onPageTransition",function(e,data){
				//TODO:将goOn这部分逻辑写成一个通用的方法例如 actionIf(fn,args)
				var goOn=true;
				if(p.opts.onPageTransition){
					goOn=p.opts.onPageTransition.call($.mobile,data);
				};
				if(!goOn) return;

				data.to.addClass( $.mobile.activePageClass + " " + data.transition +
					" in " + ( data.reverse ? "reverse" : "" ) );

				// callback - remove classes, etc
				data.to.animationComplete(function() {
					data.to.add(data.from).removeClass("out in reverse " + data.transition );
					//from在ajax之前就已经隐藏了，所以下面的交互不需要
					/*
					if( data.from ){
						data.from.removeClass( $.mobile.activePageClass );
					}
					*/
					data.pageChangeComplete();
					data.removeContainerClasses();
				});			
			});

			$window.bind("onPageChanging",function(e,data){
				//ajax请求前，先隐藏当前页，然后显示loading
				if(data.from){
					data.from.removeClass( $.mobile.activePageClass );
					//可以根据需要加上动画
					//data.from.addClass( data.transition + " out " + ( data.reverse ? "reverse" : "" ) );
				};
				//显示loading
				if(data.isAjax){
					$.mobile.pageLoading();
				};
			});



		},//setup
		teardown:function(){
			$.mobile.changePage = this.backup;
		}//teardown
	};

	pub.init=function(opts){
		p.opts=p.opts||{};
		p.changePage.setup();

	};//init

	pub.data=function(){
		return p.changePage.data;
	};//data

	return pub;

 }
 
 )(jQuery));